odoo.define("Iant.ModelFieldSelector", function (require) {
    "use strict";

    var core = require("web.core");
    var ModelFieldSelector = require("web.ModelFieldSelector");

    ModelFieldSelector.include({
        editionEvents: {
            "focusin": "_onFocusIn",
            "focusout": "_onFocusOut",
            "click .o_field_selector_close": "_onCloseClick",
            "click .o_field_selector_prev_page": "_onPrevPageClick",
            "click .o_field_selector_next_page": "_onNextPageClick",
            "click li.o_field_selector_select_button": "_onLastFieldClick",
            "change input.o_field_selector_debug": "_onDebugInputChange",
            "keyup .o_field_selector_search > input": "_onSearchInputChange",
            "mouseover li.o_field_selector_item": "_onItemHover",
            "keydown": "_onKeydown"
        },

        init: function (parent, model, chain, options){
            this._super.apply(this, arguments);
            this.options['showSearchInput'] = this.options['showSearchInput'] === undefined ? true : this.options['showSearchInput'];
            this.searchValue = '';
        },

        start: function () {
            var result = this._super.apply(this, arguments);
            this.$popover = this.$(".o_field_selector_popover");
            this.$input = this.$popover.find(".o_field_selector_popover_footer > input");
            this.$searchInput = this.$popover.find(".o_field_selector_search > input");
            return result;
        },

        _addChainNode: function (fieldName) {
            this._super.apply(this, arguments);

            this.searchValue = '';
            this.$searchInput.val('');
        },

        _onSearchInputChange: function () {
            this.searchValue = this.$searchInput.val();
            this._render();
        },

        _onKeydown: function (e) {
            if (!this.$popover.is(":visible")) return;
            var inputHasFocus = this.$input.is(":focus");
            var searchInputHasFocus = this.$searchInput.is(":focus");

            switch (e.which) {
                case $.ui.keyCode.UP:
                case $.ui.keyCode.DOWN:
                    e.preventDefault();
                    var $active = this.$("li.o_field_selector_item.active");
                    var $to = $active[e.which === $.ui.keyCode.DOWN ? "next" : "prev"](".o_field_selector_item");
                    if ($to.length) {
                        $active.removeClass("active");
                        $to.addClass("active");
                        this.$popover.focus();

                        var $page = $to.closest(".o_field_selector_page");
                        var full_height = $page.height();
                        var el_position = $to.position().top;
                        var el_height = $to.outerHeight();
                        var current_scroll = $page.scrollTop();
                        if (el_position < 0) {
                            $page.scrollTop(current_scroll - el_height);
                        } else if (full_height < el_position + el_height) {
                            $page.scrollTop(current_scroll + el_height);
                        }
                    }
                    break;
                case $.ui.keyCode.RIGHT:
                    if (inputHasFocus) break;
                    e.preventDefault();
                    var name = this.$("li.o_field_selector_item.active").data("name");
                    if (name) {
                        var field = this._getLastPageField(name);
                        if (field.relation) {
                            this._goToNextPage(field);
                        }
                    }
                    break;
                case $.ui.keyCode.LEFT:
                    if (inputHasFocus) break;
                    e.preventDefault();
                    this._goToPrevPage();
                    break;
                case $.ui.keyCode.ESCAPE:
                    e.stopPropagation();
                    this._hidePopover();
                    break;
                case $.ui.keyCode.ENTER:
                    if (inputHasFocus || searchInputHasFocus) break;
                    e.preventDefault();
                    this._selectField(this._getLastPageField(this.$("li.o_field_selector_item.active").data("name")));
                    break;
            }
        },

        _prefill: function () {
            this.pages = [];
            return this._pushPageData(this.model).then((function () {
                this._validate(true);
                return (this.chain.length ? processChain.call(this, this.chain.slice().reverse()) : Promise.resolve());
            }).bind(this));

            function processChain(chain) {
                var fieldName = chain.pop();
                var field = this._getLastPageField(fieldName);
                if (field && field.relation) {
                    if (chain.length) { // Fetch next chain node if any and possible
                        return this._pushPageData(field.relation).then(processChain.bind(this, chain));
                    } else { // Simply update the last popover page
                        return this._pushPageData(field.relation);
                    }
                } else if (field && chain.length === 0) { // Last node fetched
                    return $.when();
                } else if (!field && fieldName === "1") { // TRUE_LEAF
                    this._validate(true);
                } else if (!field && fieldName === "0") { // FALSE_LEAF
                    this._validate(true);
                } else { // Wrong node chain
                    this._validate(false);
                }
                return $.when();
            }
        },

        _render: function () {

            // Render the chain value
            this.$value.html(core.qweb.render(this.template + ".value", {
                chain: this.chain,
                pages: this.pages,
            }));

            // Toggle the warning message
            this.$valid.toggleClass('d-none', !!this.isValid());

            // Adapt the popover content
            var page = _.last(this.pages);
            var title = "";
            if (this.pages.length > 1) {
                var prevField = _.findWhere(this.pages[this.pages.length - 2], {
                    name: (this.chain.length === this.pages.length) ? this.chain[this.chain.length - 2] : _.last(this.chain),
                });
                if (prevField) title = prevField.string;
            }
            this.$(".o_field_selector_popover_header .o_field_selector_title").text(title);

            var search_lines =  _.filter(page, this.options.filter);
            var excluded_fields = [
                '__last_update', 'create_date', 'access_token', 'access_url', 'access_warning', 'activity_date_deadline',
                'activity_ids', 'activity_state', 'activity_summary', 'activity_type_id', 'activity_user_id', 'create_uid',
                'message_attachment', 'message_channel_ids', 'message_follower_ids', 'message_has_error', 'message_has_error_counter',
                'message_ids', 'message_is_follower', 'message_main_attachment_id', 'message_needaction', 'message_needaction_counter',
                'message_partner_ids', 'message_unread', 'message_unread_counter', 'signature', 'website_message_ids',
                'write_date', 'write_uid'
            ];
            var lines = _.filter(search_lines, function (item) {
                return excluded_fields.indexOf(item['name']) === -1;
            });
            if (this.searchValue) {
                var matches = fuzzy.filter(this.searchValue, _.pluck(lines, 'string'));
                lines = _.map(_.pluck(matches, 'index'), function (i) {
                    return lines[i];
                });
            }

            this.$(".o_field_selector_page").replaceWith(core.qweb.render(this.template + ".page", {
                lines: lines,
                followRelations: this.options.followRelations,
                debug: this.options.debugMode,
            }));
            this.$input.val(this.chain.join("."));
        },

        _onDebugInputChange: function () {
            var userChainStr = this.$input.val();
            var userChain = userChainStr.split(".");
            if (!this.options.followRelations && userChain.length > 1) {
                this.do_warn(_t("Relation not allowed"), _t("You cannot follow relations for this field chain construction"));
                userChain = [userChain[0]];
            }
            this.setChain(userChain).then((function () {
                this.trigger_up("field_chain_changed", {chain: this.chain});
            }).bind(this));
        },

    });

});